package com.hwg;

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.List;

/**
 * Java死锁检测器 - 用于检测Java进程中的死锁情况
 */
public class JavaDeadlockDetector {

    public static void main(String[] args) throws Exception {
        System.out.println("正在查找Java进程...");
        List<VirtualMachineDescriptor> vmds = VirtualMachine.list();

        if (vmds.isEmpty()) {
            System.out.println("未发现任何Java进程。");
            return;
        }

        System.out.println("\n可用的Java进程列表:");
        System.out.println("---------------------------------------------");
        System.out.println("PID\t进程名称");
        System.out.println("---------------------------------------------");

        for (VirtualMachineDescriptor vmd : vmds) {
            System.out.printf("%s\t%s\n", vmd.id(), vmd.displayName());
        }
        System.out.println("---------------------------------------------");

        System.out.print("\n请输入要检测死锁的Java进程PID (输入'all'检测所有进程): ");
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String input = reader.readLine().trim();

        if ("all".equalsIgnoreCase(input)) {
            // 检测所有Java进程
            for (VirtualMachineDescriptor vmd : vmds) {
                System.out.println("\n正在检测进程: " + vmd.id() + " - " + vmd.displayName());
                try {
                    detectDeadlock(vmd.id());
                } catch (Exception e) {
                    System.out.println("检测进程 " + vmd.id() + " 时出错: " + e.getMessage());
                }
            }
        } else {
            // 检测单个进程
            detectDeadlock(input);
        }
    }

    private static void detectDeadlock(String pid) throws Exception {
        VirtualMachine vm = null;
        JMXConnector connector = null;

        try {
            // 连接到目标JVM
            vm = VirtualMachine.attach(pid);
            System.out.println("已成功连接到进程 " + pid);

            // 获取JMX连接
            String connectorAddr = vm.startLocalManagementAgent();

            if (connectorAddr == null) {
                // 如果JMX未启用，尝试加载管理代理
                String javaHome = System.getProperty("java.home");
                String agent = javaHome + "/lib/management-agent.jar";
                vm.loadAgent(agent);
                connectorAddr = vm.startLocalManagementAgent();
            }

            JMXServiceURL serviceURL = new JMXServiceURL(connectorAddr);
            connector = JMXConnectorFactory.connect(serviceURL);
            MBeanServerConnection mbsc = connector.getMBeanServerConnection();

            // 获取ThreadMXBean
            ObjectName threadMXBeanName = new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
            ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy(
                    mbsc, threadMXBeanName.toString(), ThreadMXBean.class);

            // 检测死锁
            detectAndPrintDeadlocks(threadMXBean);

            // 获取进程信息
            printProcessInfo(mbsc);

        } finally {
            if (connector != null) {
                try {
                    connector.close();
                } catch (Exception e) {
                    // 忽略关闭时的异常
                }
            }

            if (vm != null) {
                vm.detach();
            }
        }
    }

    private static void detectAndPrintDeadlocks(ThreadMXBean threadMXBean) {
        System.out.println("\n开始检测死锁...");

        // 尝试使用findDeadlockedThreads（Java 6+）
        long[] deadlockedThreadIds = threadMXBean.findDeadlockedThreads();

        // 如果findDeadlockedThreads返回null，尝试使用findMonitorDeadlockedThreads
        if (deadlockedThreadIds == null) {
            deadlockedThreadIds = threadMXBean.findMonitorDeadlockedThreads();
        }

        if (deadlockedThreadIds == null || deadlockedThreadIds.length == 0) {
            System.out.println("✓ 未检测到任何死锁");
            return;
        }

        System.out.println("⚠ 检测到 " + deadlockedThreadIds.length + " 个线程处于死锁状态!");
        System.out.println("\n死锁线程详细信息:");
        System.out.println("-------------------------------------------------------");

        ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreadIds, true, true);
        for (ThreadInfo threadInfo : threadInfos) {
            if (threadInfo == null) continue;

            System.out.println("线程ID: " + threadInfo.getThreadId());
            System.out.println("线程名称: " + threadInfo.getThreadName());
            System.out.println("线程状态: " + threadInfo.getThreadState());

            System.out.println("等待锁: " + (threadInfo.getLockName() != null ? threadInfo.getLockName() : "无"));
            if (threadInfo.getLockOwnerName() != null) {
                System.out.println("锁持有者ID: " + threadInfo.getLockOwnerId());
                System.out.println("锁持有者名称: " + threadInfo.getLockOwnerName());
            }

            System.out.println("线程栈:");
            StackTraceElement[] stackTrace = threadInfo.getStackTrace();
            for (StackTraceElement element : stackTrace) {
                System.out.println("\t" + element);
            }

            System.out.println("-------------------------------------------------------");
        }

        // 可视化死锁图
        printDeadlockGraph(threadInfos);
    }

    private static void printDeadlockGraph(ThreadInfo[] threadInfos) {
        System.out.println("\n死锁关系图:");
        System.out.println("-------------------------------------------------------");

        for (ThreadInfo threadInfo : threadInfos) {
            if (threadInfo == null) continue;

            String threadName = threadInfo.getThreadName();
            String lockOwnerName = threadInfo.getLockOwnerName();
            String lockName = threadInfo.getLockName();

            if (lockOwnerName != null && lockName != null) {
                System.out.println(threadName + " 正在等待锁 [" +
                        simplifyLockName(lockName) + "] 被 " + lockOwnerName + " 占用");
            }
        }

        System.out.println("-------------------------------------------------------");
    }

    private static String simplifyLockName(String lockName) {
        // 简化锁名称，增加可读性
        if (lockName == null) return "未知锁";

        // 提取类名
        int lastDot = lockName.lastIndexOf('.');
        if (lastDot >= 0 && lastDot < lockName.length() - 1) {
            return lockName.substring(lastDot + 1);
        }

        return lockName;
    }

    private static void printProcessInfo(MBeanServerConnection mbsc) {
        try {
            // 获取JVM信息
            ObjectName runtimeMXBeanName = new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME);
            String jvmName = (String) mbsc.getAttribute(runtimeMXBeanName, "VmName");
            String jvmVersion = (String) mbsc.getAttribute(runtimeMXBeanName, "VmVersion");
            String jvmVendor = (String) mbsc.getAttribute(runtimeMXBeanName, "VmVendor");

            // 获取内存信息
            ObjectName memoryMXBeanName = new ObjectName(ManagementFactory.MEMORY_MXBEAN_NAME);
            Object heapMemoryUsage = mbsc.getAttribute(memoryMXBeanName, "HeapMemoryUsage");
            long used = (long) javax.management.openmbean.CompositeDataSupport.class
                    .cast(heapMemoryUsage).get("used");
            long max = (long) javax.management.openmbean.CompositeDataSupport.class
                    .cast(heapMemoryUsage).get("max");

            // 获取线程信息
            ObjectName threadMXBeanName = new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME);
            int threadCount = (int) mbsc.getAttribute(threadMXBeanName, "ThreadCount");
            int peakThreadCount = (int) mbsc.getAttribute(threadMXBeanName, "PeakThreadCount");

            System.out.println("\n进程基本信息:");
            System.out.println("-------------------------------------------------------");
            System.out.println("JVM名称: " + jvmName);
            System.out.println("JVM版本: " + jvmVersion);
            System.out.println("JVM供应商: " + jvmVendor);
            System.out.println("堆内存使用: " + formatSize(used) + " / " + formatSize(max));
            System.out.println("当前线程数: " + threadCount);
            System.out.println("峰值线程数: " + peakThreadCount);
            System.out.println("-------------------------------------------------------");

        } catch (Exception e) {
            System.out.println("获取进程信息时出错: " + e.getMessage());
        }
    }

    private static String formatSize(long size) {
        if (size < 1024) return size + " B";
        if (size < 1024 * 1024) return String.format("%.2f KB", size / 1024.0);
        if (size < 1024 * 1024 * 1024) return String.format("%.2f MB", size / (1024.0 * 1024));
        return String.format("%.2f GB", size / (1024.0 * 1024 * 1024));
    }
}